package com.pktechan.api.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.pktechan.api.base.MiniProgramGlobalVar;
import com.pktechan.api.entity.WxMp;
import com.pktechan.api.exception.CustomException;
import com.pktechan.api.service.CustBaseService;
import com.pktechan.api.service.MiniProgramService;
import com.pktechan.api.service.RedisService;
import com.pktechan.api.service.WxMpService;
import com.pktechan.api.utils.HttpUtil;
import com.pktechan.api.utils.RandomUtil;
import com.pktechan.api.utils.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;


/**
 * @program: cloudfloating
 * @description: 小程序接口实现
 * @author: qx.zhang
 * @create: 2021-03-08 14:55
 **/
@Service("miniProgramService")
@Transactional
public class MiniProgramServiceImpl implements MiniProgramService {
    @Autowired
    private WxMpService wxMpService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private CustBaseService custBaseService;

    private static final Logger logger = LoggerFactory.getLogger(MiniProgramServiceImpl.class);

    /**
     * 用户登录
     *
     * @param mpId 小程序mpId
     * @param code 用户小程序中获取的code
     * @return
     * @throws CustomException
     */
    @Override
    public String onLogin(String mpId, String code) throws CustomException {
        //根据MpId查询appid与appsecret
        WxMp mp = wxMpService.getByMpId(mpId);
        //组装请求地址
        String jsCode2SessionUrl = MiniProgramGlobalVar.URL_JS_CODE_2_SESSION.replace("APPID", mp.getAppId()).replace("SECRET", mp.getSecret()).replace("JSCODE", code);
        logger.debug("临时登录凭证校验接口URL=" + jsCode2SessionUrl);
        String respXML = HttpUtil.reqJsonGet(jsCode2SessionUrl);
        logger.debug("临时登录凭证校验接口调用成功,respXML=" + respXML);
        JSONObject respJO = JSONObject.parseObject(respXML);
        HashMap<String,String> respMap = new HashMap(respJO);
        logger.debug("临时登录凭证校验接口转换成Map,Map=" + respMap);
        //生成唯一字符串，作为登录态
        String miniProgramSessionId = RandomUtil.createSessionId(respJO.getString("openid"));
        //将小程序用户登录态保存到redis中。
        redisService.delLoginState(miniProgramSessionId);
        redisService.setLoginState(miniProgramSessionId, respMap);
        logger.debug("用户登录态保存成功：miniProgramSessionId=" + miniProgramSessionId);
        return miniProgramSessionId;
    }

    /**
     * 更新用户信息
     *
     * @param res            小程序中getUserInfo的响应数据
     * @param userLoginState 当前用户登录态
     * @param mpId           小程序mpId
     */
    @Override
    public void updateUserInfo(String res, String userLoginState, String mpId) throws CustomException {
        logger.debug("更新用户信息service方法，userLoginState=" + userLoginState + "--------mpId=" + mpId);
        //根据MpId查询appid与appsecret
//        WxMp mp = wxMpService.getByMpId(mpId);
        //获取用户登录信息
        HashMap<String, String> loginStateMap = (HashMap<String, String>) redisService.getLoginState(userLoginState);
        //会话密钥，用来对用户数据进行加密签名的密钥
        String sessionKey = loginStateMap.get("session_key");
        logger.debug("更新用户信息时从redis中获取的---------sessionKey====" + sessionKey);
//        //小程序中getUserInfo的响应数据转JSON
        JSONObject resJson = JSONObject.parseObject(res);
        resJson.put("openId",loginStateMap.get("openid"));
//        //不包括敏感信息的原始数据字符串，用户计算签名
//        String rawData = resJson.getString("rawData");
//        //使用sha1(rawData+sessionKey)得到的字符串，用于校验用户信息
//        String signature = resJson.getString("signature");
//        //包括敏感数据在内的完整用户信息的加密数据
//        String encryptedData = resJson.getString("encryptedData");
//        //加密算法的初始向量
//        String iv = resJson.getString("iv");
//        //数据签名校验;
//        String signature1 = SHA1Util.getSha1(rawData + sessionKey);
//        if (!signature.equals(signature1)) {
//            logger.debug("解析的数据签名：" + signature + "---------本地计算的签名：" + signature1);
//            throw new CustomException("4001", "数据签名校验不通过");
//        }
//        //用户数据解密
//        String decryptData = MiniProgramUtil.decrypt(encryptedData, sessionKey, iv);
//        JSONObject decryptDataJO = JSONObject.parseObject(decryptData);
//        //加密数据中的APPID
//        String decryptAppId = decryptDataJO.getJSONObject("watermark").getString("appid");
//        if (!mp.getAppId().equals(decryptAppId)) {
//            throw new CustomException("4002", "appId不匹配");
//        }
//        logger.debug("decryptData=" + decryptData);
        //将客户基本信息保存到
        custBaseService.saveOrUpdate4MiniProgram(resJson, mpId);
        //如果登录sesseion中未保存unionId，则此时保存
//        if (!loginStateMap.containsKey("unionid")) {
//            loginStateMap.put("unionid", resJson.getString("unionId"));
//            //将小程序用户登录态保存到redis中。
//            redisService.setLoginState(userLoginState, loginStateMap);
//        }
    }

    /**
     * 获取小程序码，适用于需要的码数量极多的业务场景。通过该接口生成的小程序码，永久有效，数量暂无限制。 更多用法详见 获取二维码。
     *
     * @param scene
     * @param page
     * @param mpId
     */
    @Override
    public byte[] getWxACodeUnLimit(String scene, String page, String mpId, Integer width) throws CustomException {
        //根据MpId查询appid与appsecret
        WxMp wxMp = wxMpService.getByMpId(mpId);
        String accessToken = wxMpService.getMpAccessToken(wxMp);
        String getWXACodeUnLimitUrl = MiniProgramGlobalVar.GET_WXA_CODE_UN_LIMIT.replace("ACCESS_TOKEN", accessToken);
        com.alibaba.fastjson.JSONObject reqJson = new com.alibaba.fastjson.JSONObject();
        reqJson.put("scene", scene);
        reqJson.put("page", page);
        reqJson.put("width", width);
        byte[] codeByte = HttpUtil.doPostSSLForCode(getWXACodeUnLimitUrl, reqJson);
        return codeByte;
    }

    /**
     * 获取小程序码，适用于需要的码数量较少的业务场景。通过该接口生成的小程序码，永久有效，有数量限制，详见获取二维码。
     *
     * @param path       扫码进入的小程序页面路径，最大长度 128 字节，不能为空；对于小游戏，可以只传入 query 部分，来实现传参效果，如：传入 "?foo=bar"，即可在 wx.getLaunchOptionsSync 接口中的 query 参数获取到 {foo:"bar"}。
     * @param queryParam
     * @param mpId
     */
    @Override
    public byte[] getWxACode(String path, String queryParam, String mpId) throws CustomException {
        //根据MpId查询appid与appsecret
        WxMp mp = wxMpService.getByMpId(mpId);
        String accessToken = wxMpService.getMpAccessToken(mp);
        String getWXACodeUnLimitUrl = MiniProgramGlobalVar.GET_WXA_CODE.replace("ACCESS_TOKEN", accessToken);
        com.alibaba.fastjson.JSONObject reqJson = new com.alibaba.fastjson.JSONObject();
        JSONObject queryJSON = JSONObject.parseObject(queryParam);
        String query = "";
        if (StringUtil.isValid(queryParam)) {
            query = "?";
        }
        Set keySet = queryJSON.entrySet();
        Iterator iterator = keySet.iterator();
//        while (iterator.hasNext()){
//            key = (String) iterator.next();
//            value = jsonObject.getString(key);
//        }
        reqJson.put("path", path);
        reqJson.put("width", 450);
        byte[] codeByte = HttpUtil.doPostSSLForCode(getWXACodeUnLimitUrl, reqJson);
        return codeByte;
    }

    /**
     * 通过沉默授权，仅返回openid
     *
     * @param wxMp
     * @param code
     * @return
     */
    @Override
    public String getBaseOpenId(WxMp wxMp, String code) {
        logger.info("已进入getBaseOpenId方法");
        String sns_token_url = MiniProgramGlobalVar.URL_SNS_TOKEN.replace("APPID", wxMp.getAppId()).replace("SECRET", wxMp.getSecret()).replace("CODE", code);
        logger.debug("sns_user_base, get openid,url=" + sns_token_url);
        String respXML = HttpUtil.reqJsonGet(sns_token_url);
        logger.debug("sns_user_base, get openid,respXML=" + respXML);
        JSONObject retJO = JSONObject.parseObject(respXML);
        return retJO.getString("openid");
    }
}
